﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using System.Timers;
using SharedDataContracts;
using CommonDataContracts;
using Microsoft.Samples.Discovery;
using System.Net;
using System.Net.Sockets;



namespace BacteriumObserver
{
    public class BacteriumObserverFacade : IBacteriumObserverFacade
    {
        private BacteriumObserver _o;
        private BacteriumReporter _r;

        private Object _cs = new Object();
        

        private Dictionary<String, BacteriumProfile> _bacteria = new Dictionary<String, BacteriumProfile>();

        private Timer _lifetimeCheckTimer;
        private const int LIFETIME_THRESHOLD = 6;

        public BacteriumObserverFacade()
        {           

            //They share the same bacteria
            _o = new BacteriumObserver() { Bacteria = _bacteria, CS = _cs };
            _r = new BacteriumReporter() { Bacteria = _bacteria, CS = _cs };

            _lifetimeCheckTimer = new Timer(5000);
            _lifetimeCheckTimer.AutoReset = true;
            _lifetimeCheckTimer.Elapsed += new ElapsedEventHandler(_lifetimeChekTimeout_Elapsed);
        }

        static void Main(string[] args)
        {
            var bacteriumFacade = new BacteriumObserverFacade();
            //#region Facade Host

           

            //BasicHttpBinding binding = new BasicHttpBinding();
            //ServiceHost facadeHost = new ServiceHost(typeof(BacteriumObserverFacade),
            //    new Uri("http://localhost:2196/BacteriumObserverFacade"));

            //facadeHost.AddServiceEndpoint(typeof(IBacteriumObserverFacade),
            //    binding,
            //    string.Empty);

            //facadeHost.Open();
            //Console.WriteLine("Facade ready.");

            //#endregion

            #region Observer Host            

            var observerBaseAddress = new UriBuilder("http://", 
                Dns.GetHostName(), 
                GetFreeTcpPort())
                .Uri;

            ServiceHost observerHost = new ServiceHost(bacteriumFacade._o,
                observerBaseAddress);

            Console.Title = "Observer: " + observerHost.BaseAddresses.FirstOrDefault();            

            observerHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
            observerHost.Extensions.Add(new CustomDiscoveryExtension());

            //bacteriumFacade._lifetimeCheckTimer.Start();
            observerHost.Open();            

            Console.WriteLine("Observer ready.");

            #endregion

            #region Reporter Host

            var reporterBaseAddress = new UriBuilder("http://",
                Dns.GetHostName(),
                GetFreeTcpPort())
                .Uri;

            ServiceHost reporterHost = new ServiceHost(bacteriumFacade._r, 
                reporterBaseAddress);

            Console.Title += "  Reporter: " + reporterHost.BaseAddresses.FirstOrDefault();    

            reporterHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());

            reporterHost.Open();
            Console.WriteLine("Reporter ready.");

            #endregion

            Console.WriteLine("Everything launched. Press enter to crash.");
            Console.ReadLine();
        }

        void _lifetimeChekTimeout_Elapsed(object sender, ElapsedEventArgs e)
        {
            //var deadEndpoints = new List<String>();
            
            //lock (_cs)
            //{
            //    foreach (var bacteriumProfile in _bacteria)
            //    {
            //        var timestamp = bacteriumProfile.Value.Events.Keys.Max();
            //        var averageTimestamp = _bacteria.Values.Where(b => !b.Equals(bacteriumProfile)).Average(b => b.Events.Keys.Max());

            //        if (averageTimestamp - timestamp> LIFETIME_THRESHOLD)
            //            deadEndpoints.Add(bacteriumProfile.Key);
            //    }
            //}

            ////Impossible to do it in a single loop
  
            //foreach (var deadEndpoint in deadEndpoints)
            //{
            //    _o.UnregisterFromDomain(deadEndpoint);
            //}
            

        }

        #region Observer

        public void RegisterWithDomain(String endpoint, BacteriumSnapshot b)
        {
            _o.RegisterWithDomain(endpoint, b);
        }


        public void UpdateBacteriumProperty(String endpoint, String name, String value) 
        {
            _o.UpdateBacteriumProperty(endpoint, name, value);
        }


        public String GetNeutralBacterium(String endpoint) 
        {
            return _o.GetNeutralBacterium(endpoint);
        }


        public Boolean GetTeamBalance(String endpoint)
        {
            return _o.GetTeamBalance(endpoint); 
        }


        public String GetWeakestEnemy(String endpoint) 
        { 
            return _o.GetWeakestEnemy(endpoint);
        }


        public Boolean GetPowerRatio(String endpoint) 
        {
            return _o.GetPowerRatio(endpoint); ; 
        }


        public String GetWeakestAlly(String endpoint) 
        { 
            return _o.GetWeakestAlly(endpoint); 
        }

        #endregion

        #region Reporter

        public List<BacteriumProfile> GetDomainSnapshot()
        {
            return _r.GetDomainSnapshot();
        }

        #endregion

        static int GetFreeTcpPort()
        {
            TcpListener l = new TcpListener(IPAddress.Parse("127.0.0.1"), 0);
            l.Start();
            int port = ((IPEndPoint)l.LocalEndpoint).Port;
            l.Stop();
            return port;
        }
    }
}
